package czopyk.model.cost;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.Validate;
import org.apache.log4j.Logger;

import czopyk.model.request.CompositeRequest;
import czopyk.model.request.LoadClass;
import czopyk.model.request.Request;
import czopyk.model.structure.CenterClass;

public class CostMapper {

	private static final Logger lgr = Logger.getLogger(CostMapper.class);

	private final Map<CenterClass, Map<LoadClass, DistributionId>> costMapping = new HashMap<CenterClass, Map<LoadClass, DistributionId>>();

	private DistributionManager distributionManager;

	public void setDistributionManager(DistributionManager distributionManager) {
		this.distributionManager = distributionManager;
	}

	public boolean isCenterCapableOfServingLoadClass(CenterClass cc, LoadClass lc) {
		boolean is;
		Map<LoadClass, DistributionId> map = costMapping.get(cc);

		if (map == null) {
			is = false;
		} else {
			is = map.containsKey(lc);
		}

		lgr.debug("Center of class '" + cc.getValue() + "' capable of serving "
				+ lc + "? " + is);
		return is;
	}

	public Set<Request> getSupportedRequests(Request r, CenterClass centerClass) {
		Set<Request> s = new HashSet<Request>();

		if (r.isDone()) {
			return Collections.emptySet();
		}

		if (r instanceof CompositeRequest) {
			for (Request e : ((CompositeRequest) r).getComponents()) {
				s.addAll(getSupportedRequests(e, centerClass));
			}
		}

		if (isCenterCapableOfServingLoadClass(centerClass, r.getLc())) {
			s.add(r);
		}

		return s;
	}

	public Double getCost(LoadClass loadClass, CenterClass centerClass) {
		DistributionId distributionId = costMapping.get(centerClass).get(loadClass);
		if (distributionId == null) {
			return null;
		}
		double d = distributionManager.getDistributionDoubleValue(distributionId);
		if (lgr.isDebugEnabled()) {
			lgr.debug("getting cost for " + loadClass + " in centerClass: " + centerClass + " -> " + d);
		}
		if (d < 0.0) {
			lgr.warn("obtained negative value from distribution '" + distributionId
					+ "'");
		}
		return Math.max(0.0, d);
	}

	public void registerCostMapping(LoadClass lc, CenterClass cc,
			DistributionId distributionId) {

		Validate.notNull(lc);
		Validate.notNull(cc);

		Map<LoadClass, DistributionId> map = costMapping.get(cc);
		if (map == null) {
			map = new HashMap<LoadClass, DistributionId>();
			costMapping.put(cc, map);
		}

		map.put(lc, distributionId);

		lgr.debug("registered cost mapping " + lc + " for '" + cc.getValue() + "'");
	}

	public void validate() {
		// TODO poprawna walidacja
		/*
		 * for (CenterClass cc : costMapping.keySet()) { for (RequestType rt :
		 * costMapping.get(cc).keySet()) { if
		 * (!Sets.symmetricDifference(costMapping.get(cc).get(rt).keySet(),
		 * requestFactory.getClassesForType(rt)).isEmpty()) { throw new
		 * IllegalStateException(
		 * "Cost mapping not defined for all load classes of load type '" + rt +
		 * "' of center class '" + cc + "'"); } } }
		 */
	}

}
